using UnityEngine;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Prota.Unity
{
    [DisallowMultipleComponent]
    [ExecuteInEditMode]
    public class SpriteGroupTool : MonoBehaviour
    {
        public bool declineRecursive = false;
        
        public bool setDepth = false;
        public bool applyDepthOnSelf = false;
        public bool depthForward = true;
        public float depthFrom = 0f;
        public float depthStep = 0.01f;
        
        public bool setLayer = false;
        public int layerIndex = 0;
        
        public bool setSortingLayer = false;
        public int sortingLayerIndex = 0;
        
        public bool setParallax = false;
        public Transform parallaxCameraAnchor;
        public bool parallaxForward = true;
        public float parallaxStart = 0;
        public float parallaxDistance = 1;
        public AnimationCurve parallaxDistanceCurve = AnimationCurve.Linear(0, 0, 1, 1);
        
        void OnTransformChildrenChanged()
        {
            UpdateAll();
        }
        
        void OnEnable()
        {
            UpdateAll();
        }
        
        public void UpdateAll()
        {
            if(setDepth)
            {
                float depth = depthFrom;
                SetDepth(this.transform, ref depth);
            }
            
            if(setLayer)
            {
                SetLayer(this.transform);
            }
            
            if(setSortingLayer)
            {
                SetSortingLayer(this.transform);
            }
            
            if(setParallax)
            {
                if(parallaxCameraAnchor != null) 
                {
                    SetParallax(this.transform);
                }
            }
            else
            {
                RemoveParallax(this.transform);
            }
            
        }
        
        // ====================================================================================================
        // ====================================================================================================
        
        
        void SetDepth(Transform root, ref float depth, float parentDepth = 0)
        {
            if(RecursingDeclined(root)) return;
            if(applyDepthOnSelf || root != this.transform)
            {
                root.localPosition = root.localPosition.WithZ(depth - parentDepth);
                depth += depthStep * (depthForward ? 1 : -1);
            }
            foreach (Transform child in root) SetDepth(child, ref depth, root.localPosition.z);
        }
        
        // ====================================================================================================
        // ====================================================================================================
        
        void SetLayer(Transform root)
        {
            if(RecursingDeclined(root)) return;
            root.gameObject.layer = layerIndex;
            foreach (Transform child in root) SetLayer(child);
        }
        
        // ====================================================================================================
        // ====================================================================================================
        
        void SetSortingLayer(Transform root)
        {
            if(RecursingDeclined(root)) return;
            var renderer = root.GetComponent<SpriteRenderer>();
            if(renderer != null)
            {
                renderer.sortingLayerID = SortingLayer.layers[sortingLayerIndex].id;
            }
            foreach (Transform child in root) SetSortingLayer(child);
        }
        
        // ====================================================================================================
        // ====================================================================================================
        
        void SetParallax(Transform root)
        {
            if(RecursingDeclined(root)) return;
            int total = root.childCount;
            int index = 0;
            foreach(var child in root.AsList<Transform>())
            {
                var parallax = child.GetOrCreate<Parallax>();
                var ratio = index / (float)(total - 1);
                var parallaxRatio = this.parallaxDistanceCurve.Evaluate(ratio);
                float thisDistance = (parallaxStart, parallaxStart + parallaxDistance).Lerp(parallaxRatio);
                thisDistance *= parallaxForward ? 1 : -1;
                parallax.distance = thisDistance;
                index++;
            }
        }
        
        private void RemoveParallax(Transform root)
        {
            if(RecursingDeclined(root)) return;
            foreach(var child in root.AsList<Transform>())
            {
                if(child.TryGetComponent<Parallax>(out var p))
                {
                    DestroyImmediate(p);
                }
            }
        }
        
        // ====================================================================================================
        // Utils
        // ====================================================================================================
        
        
        bool RecursingDeclined(Transform root)
        {
            if(root != this.transform && root.TryGetComponent<SpriteGroupTool>(out var sgt) && sgt.declineRecursive) return true;
            return false;
        }
        
    }
}
